<!-- Last Updated: Friday May 31, 2002 Version 0.7-->
<html>
  <head>
    <title>SAX for Pascal Demo - Filling a TreeView</title>
  </head>
  <body>
    <div width="100%" align="right">
      <a href="http://xml.defined.net/SAX/">SAX for Pascal Home</a>
      &#160;
      <a href="http://xml.defined.net/SAX/docs">SAX for Pascal Documentation</a>
    </div>
    <h1>Filling a TreeView</h1>
    <p>This demo is designed to show you how to fill a TreeView that shows
    the document structure. Primarily you should pay attention to how
    the items are added. Throughout the parse, the CurrNode is updated. This
    allows the program to know where it should add each item in the tree.
    In general, SAX does not maintain state information (where it is in the
    tree with regard to parent/child context relationships). This means that
    you have to keep track of that information if it is important to you.
    </p>
    <p>In this demo, we will skip all of the basic project setup information.
    If you need to know more about the project setup, please see the
    "Choosing a Parser Demo" or the "Simple Parsing Demo".
    </p>
    <h2>Setting up the form</h2>
    <p>For this demo we will need to add a few components to
    the form. First we will need a ComboBox to choose our parser. Second
    we'll add a TreeView for our output. In our demo we have also added
    an ImageList so that our TreeView nodes will have images for attributes
    and elements. Next we will need a listbox to show
    our error messages. Next we will need a button to start our parsing. We also
    want to add an OpenDialog so that we can search for the file.
    Finally we will need to add a <code>TSAXContentHandler</code>
    and a <code>TSAXErrorHandler</code>. The SAX components are installed
    to the SAX tab in the Component Palette by default.</p>
    <h2>Events</h2>
    <p>Now that you have added all of the components let's set up some events.
    The event handlers for the <code>TSAXErrorHandler</code> will work exactly
    as they did in our "Simple Parsing Demo", so we won't explain those here.
    Our FormCreate event will work exactly as it did in the "Choosing a Parser
    Demo" as well. The important events for this demo are the OnClick event for
    <code>Button1</code> and the OnStartElement and OnEndElement events
    for <code>TSAXContentHandler1</code>. Let's begin with the OnClick event
    handler:
    </p>
    <p>
      <pre>
        procedure TForm1.Button1Click(Sender: TObject);
        var
          XMLReader : IXMLReader;
          XMLBufReader: IBufferedXMLReader;
          Vendor: TSAXVendor;
          ContentHandler: IContentHandler;
        begin
          TreeView1.Items.Clear;
          Listbox1.Items.Clear;
          CurrNode:= nil;
          ContentHandler:= SAXContentHandler1;
          if (OpenDialog1.Execute) then
          begin
            Vendor:= GetSAXVendor(ComboBox1.Items[ComboBox1.ItemIndex]);
            if Vendor is TBufferedSAXVendor then
            begin
              XMLBufReader:= TBufferedSAXVendor(Vendor).BufferedXMLReader;
              XMLBufReader.setContentHandler(Adapt(ContentHandler, XMLBufReader));
              XMLBufReader.setErrorHandler(SAXErrorHandler1);
              XMLBufReader.parse(OpenDialog1.FileName);
              XMLBufReader:= nil;
            end else
            begin
              XMLReader:= Vendor.XMLReader;
              XMLReader.setContentHandler(ContentHandler);
              XMLReader.setErrorHandler(SAXErrorHandler1);
              XMLReader.parse(OpenDialog1.FileName);
              XMLReader:= nil;
            end;
          end;
        end;
      </pre>
    </p>
    <p>We begin by clearing the TreeView and ListView so that our output is
    fresh. Next we set CurrNode to nil. Wait a second, what is CurrNode? CurrNode
    is our temporary <code>TTreeNode</code> that we will use while we are
    building the tree. We will add this variable declaration to our Form's
    private section:</p>
    <p>
      <pre>
        private
          CurrNode : TTreeNode;
        end;
      </pre>
    </p>
    <p>Finally we get a reference to our ContentHandler as an interface so
    that we can use it later in the function. After we have initialized all of
    our variables, we are ready to begin parsing. The rest of the code works
    exactly as it did in the "Simple Parsing Demo." Remember, in order to
    use Buffered Vendors with our normal ContentHandler, we need to use an
    adapter from the SAXAdapters unit.
    </p>
    <p>Now that we have created the parsing code we need to add the OnStartElement
    and OnEndElement event handlers. Let's look at the OnStartElement handler first:
    </p>
    <p>
      <pre>
        procedure TForm1.SAXContentHandler1StartElement(Sender: TObject;
          const NamespaceURI, LocalName, QName: WideString;
          const Atts: IAttributes);
        var I : Integer;
            ANode : TTreeNode;
        begin
          // Set the node
          if (CurrNode = nil) then
            CurrNode:= TreeView1.Items.Add(nil, qName)
          else
            CurrNode:= TreeView1.Items.AddChild(CurrNode, qName);
          // Set the image
          CurrNode.ImageIndex:= 0;
          // Add the attributes
          if (Atts &lt;&gt; nil) then
          begin
            for I:= 0 to Atts.getLength - 1 do
            begin
              ANode:= TreeView1.Items.AddChild(CurrNode, Atts.getQName(I));
              ANode.ImageIndex:= 1;
              ANode.SelectedIndex:= 1;
            end;
          end;
        end;
      </pre>
    </p>
    <p>
      The first thing we need to do is to check if CurrNode is nil. Remember
      we set CurrNode to nil before we began our parse. If CurrNode is nil then
      this is the first time that the OnStartElement callback has ocurred. This
      indicates that we need to add the root node to our TreeView. Otherwise
      we need to add a new node as a child of CurrNode. As we add the item,
      we also assign CurrNode so that we remember where we are at in the
      TreeView. Finally we set the ImageIndex so that we can tell that it is
      an element in the TreeView.
    </p>
    <p>
      After we have created the basic <code>TTreeNode</code> for the element,
      we can loop through the attributes and add each of those. Again, we will
      add the items as children of CurrNode. Now let's look at the OnEndElement
      handler:
    </p>
    <p>
      <pre>
        procedure TForm1.SAXContentHandler1EndElement(Sender: TObject;
          const NamespaceURI, LocalName, QName: WideString);
        begin
          // Expand the item
          if (CurrNode &lt;&gt; nil) then
            CurrNode.Expand(false);
          // Set the node
          CurrNode:= CurrNode.Parent;
        end;
      </pre>
    </p>
    <p>Again, our primary interest is tracking the CurrNode. All we need to
    do is set the CurrNode to it's Parent Node. Before we have done this though
    we expand that section of the TreeView.</p>
    <h2>How does it work?</h2>
    <p>
      Just in case you don't have a complete understanding of how this works,
      we will break it down into each step. Remember, as the SAX parser is parsing
      the document it will fire an OnStartElement event at the start of every
      element it encounters in the document. It will fire and OnEndElement at
      the end of each element it encounters. For now, we will not go in to detail
      about other events that are not being handled in this demo. The following
      is a summary of what happens when parsing <code>Document1.xml</code>
      included with this demo.
    </p>
    <p>
      <ol>
        <li>
          The parser first encounters the root element <code>Document</code> in
          the XML document. The parser will immediately fire an OnStartElement
          event. Within the OnStartElement handler, CurrNode is initially nil so
          we add the root item to the TreeView. We add the QName (Qualified Name)
          which is a combination of the element name and namespace prefix. We
          also set CurrNode equal to the new <code>TTreeNode</code>. So at this
          point CurrNode points to the first and only node in the TreeView,
          "Document".
        </li>
        <li>
          Before we receive the OnEndElement event, the parser encounters another
          element in the XML document. This new element, "Element1", is a child
          element of "Document". Again we receive on OnStartElement event. Remember
          CurrNode points to the first <code>TTreeNode</code> in our
          TreeView, so instead of adding a root node, we will create a new item
          that is a child of our CurrNode. Again we assign CurrNode to our new
          item in the TreeView. This time, however, we must also add nodes for
          the attributes. Notice, that we do not change the value of CurrNode
          when adding the attribute items. So, now we have received two OnStartElement
          events but no OnEndElement events. Also we have five nodes in our
          TreeView: "Document", its child "Element1", and the three attribute
          nodes "attribute1", "attribute2" and "attribute3". CurrNode still
          points to "Element1".
        </li>
        <li>
          Again, before we receive any OnEndElement events, we receive another
          OnStartElement event for the element "Element2". Remember
          CurrNode points the "Element1" <code>TTreeNode</code> in our
          TreeView, so instead of adding a root node, we will create a new item
          that is a child of our CurrNode. Again we assign CurrNode to our new
          item in the TreeView, so CurrNode points to "Element2".
        </li>
        <li>
           At last we receive an OnEndElement event as the parser reaches the
           closing tag in the XML document. CurrNode is pointing to our
           "Element2" node in the TreeView, so we expand the item (here this
           does nothing as the "Element2" node has no children) and
           point CurrNode to its parent. Just to be clear: within the XML
           document the "Element2" element is complete because the parse has
           reached the closing tag. Because it is complete, it will have no
           other children elements-- any new elements will be children of
           "Element1". Therefore, in order to make the TreeView match we had
           to change the CurrNode to point to the "Element1" node within the
           TreeView.
        </li>
        <li>
           Next we receive an OnStartElement event as the parser reaches the
           start tag for "Element3" in the XML document. CurrNode is pointing
           to our "Element1" node in the TreeView, when we add the item. This
           means that the "Element3" node is a sibling to "Element2" in our
           TreeView (just as they are in the XML document). At the end of the
           event the CurrNode points to the "Element3" node in the TreeView.
        </li>
        <li>
           It is important to note that we receive an immediate OnEndElement
           event for "Element3". In the XML document it appears as
           <code>&lt;Element3/&gt;</code> which is the shorthand XML for an
           empty element. Even empty elements will trigger OnStartElement and
           OnEndElement events.
        </li>
      </ol>
    </p>
    <p>
      At this point you should see how the events are functioning to fill the
      TreeView. The events continue in this manner until the end of the
      document is reached. If the parser encounters an error, it will stop
      firing the events. The TreeView will be filled until the point of the error
      in the document.
    </p>
  </body>
</html>

